package bamboo.swt

import org.eclipse.swt._
import org.eclipse.swt.graphics._
import org.eclipse.swt.widgets._
import org.eclipse.swt.layout._
import org.eclipse.swt.events._

trait SWTApplication {
  
  val display = new Display()
  val shell = new Shell(display)
  onDispose(shell) {
    dispose()
  }
  
  def initialize(): Unit

  def main(args : Array[String]) : Unit = {
    try {
      initialize()
      mainLoop()
    } catch {
      case e @ _ => e.printStackTrace
    }
  }
  
  def mainLoop() = messageLoop(shell)
  
  def messageLoop(shell: Shell) = {
    while (!shell.isDisposed) {
      try {
        if (!display.readAndDispatch()) display.sleep()
      } catch {
        case e@_ => loopError(e)
      }
    }
  }
  
  def asyncUI(block: => Unit) {
    display.asyncExec(new Runnable() {
      def run() = block
    })
  }
  
  def loopError(x: Throwable) {
    x.printStackTrace()
  }
  
  def dispose() = {
    display.dispose()
  }
  
  def listener(code: Event => Unit) = new Listener {
    override def handleEvent(event: Event) = code(event)
  }  
  
  def menuItem(menu: Menu, label: String) (code: => Unit) = {
    val mi = new MenuItem(menu, SWT.PUSH)
    mi.setText(label)
    mi.addListener(SWT.Selection, listener(event => code))
    mi
  }
  
  def separator(menu: Menu) {
    new MenuItem(menu, SWT.SEPARATOR)
  }
  
  def subMenu(menu: Menu, text: String) = {
    val subMenu = new Menu(menu)
    val item = new MenuItem(menu, SWT.CASCADE)
    item.setMenu(subMenu)
    item.setText(text)
    subMenu
  }
  
  def averageCharDimensions(text: Text) = {
    val gc = new GC(text)
    val fm = gc.getFontMetrics
    val charWidth = fm.getAverageCharWidth
    val charHeight = fm.getHeight
    gc.dispose()
    (charWidth, charHeight)
  }
  
  def prompt(title: String): String = prompt(title, 20, 1)
  
  def prompt(title: String, cols: Int, rows: Int): String = {
    
    val dialog = new Shell(shell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL)
    dialog.setText(title)
    
    val layout = new FormLayout()
    layout.marginWidth = 5
    layout.marginHeight = 5
    dialog.setLayout(layout)
    
    val label = new Label(dialog, SWT.NONE)
    label.setText(title)
    val style = if (rows > 1) SWT.MULTI else SWT.NONE
    val text = new Text(dialog, style | SWT.BORDER)
    val (charWidth, charHeight) = averageCharDimensions(text)
    val size = text.computeSize(charWidth * cols, charHeight * rows)
    val data = new FormData(size.x, if (rows == 1) SWT.DEFAULT else size.y)
    data.left = new FormAttachment(label, 5)
    if (rows == 1) {
      data.top = new FormAttachment(label, 0, SWT.CENTER)
    }
    text.setLayoutData(data)
    
    var result: String = null
    val ok = new Button(dialog, SWT.PUSH)
    ok.setText("OK")
    
    onSelection(ok) {
      result = text.getText()
      dialog.close
    }
    val okData = new FormData()
    okData.top = new FormAttachment(label, 10, SWT.BOTTOM)
    okData.left = new FormAttachment(label, 20, SWT.CENTER)
    ok.setLayoutData(okData)
    
    dialog.setDefaultButton(ok)
    
    dialog.pack()
    dialog.open()
    messageLoop(dialog)
    
    return result
  }
  
  def every(time: int) (block: => Unit) = {
    display.timerExec(time, new Runnable() {
      def run() {
        block
        display.timerExec(time, this)
      }
    })
  }
  
  def loadImage(image: String) = {
    val resource = getClass().getResourceAsStream(image)
    if (resource eq null) throw new IllegalArgumentException("Image resource '" + image + "' not found.")
    new Image(display, resource)
  }
  
  def onShow(widget: Widget) (block: => Unit) = {
    widget.addListener(SWT.Show, listener(event => block))
  }
  
  def onSelection(widget: Widget) (block: => Unit) = {
    widget.addListener(SWT.Selection, listener(event => block)) 
  }
  
  def onMenuDetect(widget: Widget) (block: => Unit) = {
    widget.addListener(SWT.MenuDetect, listener(event => block)) 
  }
  
  def onDispose(widget: Widget) (block: => Unit) = {
    shell.addDisposeListener(new DisposeListener {
      override def widgetDisposed(e: DisposeEvent) = {
        block
      }
    })
  }
}